package lib;

import static util.Constant.FILE_EVENTS;
import static util.Constant.PATH_DATA;
import static util.Constant.STR_NAME_NULL;
import static util.Tool.getMaxID;
import io.InOutput;

import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;

import util.Comparator;
import base.Event;
import base.Tag;

/**
 * The EvetManager {@code class}. Manage the events
 * 
 * @author Peter NGO, Guillaume POUSSEL and Mathieu RUL
 * @version 1.0
 */
public class EventManager {

	/**
	 * The {@code EventManager} instance.
	 */
	private static EventManager managerInstance;

	/**
	 * Get an instance of {@code EventManager}.
	 * 
	 * @return an instance of {@code EventManager}.
	 */
	public static EventManager getInstance() {
		if (managerInstance == null) {
			managerInstance = new EventManager();
			;
		}
		return managerInstance;
	}

	/**
	 * The {@code Hashtable} containing the Event object.
	 */
	private Hashtable<Integer, Object> eventTable;

	/**
	 * The {@code String} containing the path of Event data.
	 */
	private String path = PATH_DATA + FILE_EVENTS;

	/**
	 * Constructor of the {@code class}. Create the event manager.
	 */
	private EventManager() {
		initialize();
		try {
			this.load();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Add a Tag to an Event.
	 * 
	 * @param tag
	 *            The Tag.
	 * @param event
	 *            The Event.
	 * 
	 * @return {@code true} if the Tag was added successfully; {@code false}
	 *         otherwise.
	 */
	public boolean addTag(Event event, Tag tag) {
		return this.addTag(event.getID(), tag.getID());
	}

	/**
	 * Add a Tag to an Event.
	 * 
	 * @param tagID
	 *            The Tag ID.
	 * @param eventID
	 *            The Event ID.
	 * 
	 * @return {@code true} if the Tag was added successfully; {@code false}
	 *         otherwise.
	 */
	public boolean addTag(Integer eventID, Integer tagID) {
		boolean retour = ((Event) eventTable.get(eventID)).add(tagID);
		save();
		return retour;
	}

	/**
	 * Create a new Event from a name.
	 * 
	 * @param name
	 *            The Event name.
	 * 
	 * @return Integer the ID of created event
	 * 
	 * @throws IllegalArgumentException
	 */
	public Event create(String name) {
		if (name == null || name.trim().isEmpty())
			throw new IllegalArgumentException(STR_NAME_NULL);
		Integer ID = getMaxID(eventTable) + 1;
		Event event = new Event(ID, name, new HashSet<Integer>());
		eventTable.put(ID, event);
		save();
		return event;
	}

	/**
	 * Get an Event from his ID.
	 * 
	 * @param ID
	 *            the Event ID.
	 */
	public Event get(Integer ID) {
		return (eventTable.get(ID) == null ? null : (Event) eventTable.get(ID));
	}

	public Set<Event> getAll() {
		Set<Event> events = new HashSet<Event>();
		for (Object obj : eventTable.values()) {
			events.add((Event) obj);
		}
		return events;
	}

	/**
	 * Initialize the instance.
	 */
	public void initialize() {
		eventTable = new Hashtable<Integer, Object>();
	}

	/**
	 * Load the database in event table.
	 * 
	 * @throws Exception
	 */
	private void load() throws Exception {
		eventTable = InOutput.read(path);
	}

	/**
	 * Remove an Event.
	 * 
	 * @param ID
	 *            the Event ID.
	 */
	public Event remove(Integer ID) {
		Event retour = (Event) eventTable.remove(ID);
		save();
		return retour;
	}

	/**
	 * Remove a Tag from an Event.
	 * 
	 * @param tag
	 *            The Tag.
	 * @param event
	 *            The Event.
	 * 
	 * @return {@code true} if the Tag was removed successfully; {@code false}
	 *         otherwise.
	 */
	public boolean removeTag(Event event, Tag tag) {
		return this.removeTag(event.getID(), tag.getID());
	}

	/**
	 * Remove a tag from an Event.
	 * 
	 * @param tagID
	 *            The Tag ID.
	 * 
	 * @param eventID
	 *            The Event ID.
	 * 
	 * @return {@code true} if the Tag was removed successfully; {@code false}
	 *         otherwise.
	 */
	public boolean removeTag(Integer eventID, Integer tagID) {
		boolean retour = ((Event) eventTable.get(eventID)).remove(tagID);
		save();
		return retour;
	}

	/**
	 * Save the event table in database.
	 */
	public void save() {
		InOutput.write(eventTable, path);
	}

	/**
	 * Search Events with a name.
	 * 
	 * @param name
	 *            the tag Name.
	 * 
	 * @throws Exception
	 */
	public Event[] searchByName(String name) throws Exception {
		for (Object tagObj : eventTable.values()) {
			Event event = (Event) tagObj;
			if (event.getName().equals(name))
				return new Event[] { event };
		}

		Set<Event> events = new HashSet<Event>();
		for (Object tagObj : eventTable.values()) {
			Event event = (Event) tagObj;
			if (Comparator.compare(event.getName(), name))
				events.add(event);
		}

		return events.toArray(new Event[events.size()]);
	}

	/**
	 * Set the path.
	 * 
	 * @param path
	 *            the path to set.
	 */
	public void setPath(String path) {
		this.path = path;
	}
}
